use std::{env, fs, process::Command};

use bindgen::Builder;
use build_print::info;
use cc::Build;

fn add_irq(build: &mut Build) {
    let target = env::var("TARGET").unwrap();

    let file_name = match target.as_str() {

        "thumbv6m-none-eabi" => { 
            "irq_armv6m.S" 
        }, //CM0

        "thumbv7m-none-eabi" => {
            "irq_armv7m.S"
        }

        "thumbv7em-none-eabihf" | // + FPU + DSP
        "thumbv7em-none-eabi" => {
            "irq_armv7m.S"
        }, // + DSP

        _ => panic!("invalid target: {}", target),
    };

    let path = format!("./c_src/rtos/gcc/{}", file_name);

    build.file(&path);
}

fn config(build: &mut Build) {
    if let Ok(value) = env::var("OS_DYNAMIC_MEM_SIZE") {
        info!("OS_DYNAMIC_MEM_SIZE: {}", value);
        build.define("OS_DYNAMIC_MEM_SIZE", value.as_str());
    }
    if let Ok(value) = env::var("OS_TICK_FREQ") {
        info!("OS_TICK_FREQ: {}", value);
        build.define("OS_TICK_FREQ", value.as_str());
    }
}

fn build_cmsis_rtos2() {
    let mut build = Build::new();

    #[cfg(feature = "stm32f10x")]
    {
        build.define("STM32F10X_HD", "");
        build.define("STM32F10X", "");
    }

    #[cfg(feature = "stm32f4")]
    {
        build.define("STM32F40_41xxx", "");
        build.define("STM32F4", "");

        #[cfg(feature = "pllm_25")]
        build.define("PLL_M", "25");

        #[cfg(feature = "pllm_8")]
        build.define("PLL_M", "8");
    }

    build.include("./c_src/CMSIS");
    build.include("./c_src/rtos");

    add_irq(&mut build);

    let mut dir = fs::read_dir("./c_src/rtos").unwrap();
    while let Some(Ok(entry)) = dir.next() {
        let ft = entry.file_type().unwrap();
        if !ft.is_file() {
            continue;
        }
        let name = entry.file_name();
        let name = name.to_string_lossy();
        if !name.ends_with(".c") {
            continue;
        }
        build.file(entry.path());
    }

    #[cfg(feature = "stm32f10x")]
    build.file("./c_src/device/stm32f10x/system_stm32f10x.c");

    #[cfg(feature = "stm32f4")]
    build.file("./c_src/device/stm32f4/system_stm32f4xx.c");

    config(&mut build);

    build.compile("cmsis_rtos2");
}

fn exec_compiler(cmd: &str) -> String {
    let cc = Build::new().get_compiler();
    let path = cc.path().to_string_lossy().to_string();
    let vec = Command::new(path)
        .arg(cmd)
        .output()
        .unwrap()
        .stdout;
    String::from_utf8(vec).unwrap().trim().to_string()
}

fn bind_header() {
    let rs_path = format!("{}/{}", env::var("OUT_DIR").unwrap(), "rtx_os.rs");
   // let sysroot = exec_compiler("-print-sysroot");
    let inc = exec_compiler("-print-file-name=include");
    info!("compiler include path=> {}", inc);
    
    let builder = Builder::default()
        .clang_arg("-nostdinc")
        .clang_arg("-ffreestanding")
        .clang_arg(format!("-I{}", inc))
        .use_core()
        .rustified_enum("osPriority_t")
        .rustified_enum("osTimerType_t")
        .rustified_enum("osStatus_t")
        .rustified_enum("osKernelState_t")
        .rustified_enum("osThreadState_t")
        .header("./c_src/rtos/rtx_os.h");

    #[cfg(feature = "stm32f10x")]
    let builder = builder.header("./c_src/device/stm32f10x/system_stm32f10x.h");

    #[cfg(feature = "stm32f4")]
    let builder = builder.header("./c_src/device/stm32f4/system_stm32f4xx.h");

    builder.generate().unwrap().write_to_file(&rs_path).unwrap();
}

fn main() {
    #[cfg(all(feature = "stm32f10x", feature = "stm32f4"))]
    panic!("select device fail");

    #[cfg(any(feature = "stm32f10x", feature = "stm32f4"))]
    {
        bind_header();
        build_cmsis_rtos2();
    }
}
